﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Diagnostics;

namespace EventScavenger
{
    internal class MainReaperService
    {
        #region Private vars
        private static Mutex reaperJobUpdatingMutex = new Mutex();
        private int waitingTimeDecrement = 2000;
        private EventScavengerDAL eventLogDbDAL;
        private Collector thisCollector;
        private List<ReaperJob> reaperJobs = new List<ReaperJob>();
        private DateTime lastImportLogDtTm = new DateTime(2000, 1, 1);
        private Thread importLogsThread = null; 
        #endregion

        #region Performance counters
        private PerformanceCounter machineLogTotalCount = null;
        private PerformanceCounter machineLogEnabledCount = null;
        private PerformanceCounter eventEntriesPerSecond = null;
        private PerformanceCounter errorsPerSecond = null;
        private PerformanceCounter dbAccessPerSecond = null;
        private PerformanceCounter dbInsertsPerSecond = null;
        private PerformanceCounter dbDuplicateInsertsPerSecond = null;
        private PerformanceCounter entriesImportedPerSecond = null;

        private string perfCounterCollectorInstance = "Event Reaper - X";
        #endregion

        #region Properties
        public EventLog Log { get; set; }
        public string CollectorName { get; set; }
        public bool Running { get; set; }
        public bool Paused { get; set; }
        public int SQLCommandTimeout { get; set; }
        public string SQLServer { get; set; }
        public string Database { get; set; }
        public string UserName { get; set; }
        public string Password { get; set; }
        public bool TrustServerCertificate { get; set; }
        #endregion

        #region Run
        internal void Run()
        {
            try
            {
                #region SQL Server connection set up
                if (string.IsNullOrEmpty(SQLServer) || SQLServer.Length == 0)
                {
                    throw new Exception("SQL Server not set up in config file!");
                }
                if (string.IsNullOrEmpty(Database) || Database.Length == 0)
                {
                    throw new Exception("SQL Database not set up in config file!");
                }
                try
                {
                    LogExtendedMessage("Setting up connection", EventLogEntryType.Information);
                    eventLogDbDAL = new EventScavengerDAL();
                    eventLogDbDAL.CommandTimeout = SQLCommandTimeout;
                    eventLogDbDAL.Server = SQLServer;
                    eventLogDbDAL.Database = Database;
                    eventLogDbDAL.UserName = UserName;
                    eventLogDbDAL.Password = Password;
                    eventLogDbDAL.TrustServerCertificate = TrustServerCertificate;
                    eventLogDbDAL.SetConnection();
                    eventLogDbDAL.RaiseDBAccess += new HenIT.Data.SqlClient.RaiseDBAccessDelegate(eventLogDbDAL_RaiseDBAccess);
                    eventLogDbDAL.RaiseDBAccessErrors += new HenIT.Data.SqlClient.RaiseDBAccessDelegate(eventLogDbDAL_RaiseDBAccessErrors);
                    eventLogDbDAL.RaiseDBAccessInsertDuplicates += new HenIT.Data.SqlClient.RaiseDBAccessDelegate(eventLogDbDAL_RaiseDBAccessInsertDuplicates);
                    eventLogDbDAL.RaiseDBAccessInserts += new HenIT.Data.SqlClient.RaiseDBInsertsDelegate(eventLogDbDAL_RaiseDBAccessInserts);
                    eventLogDbDAL.RaiseInfoMessage += new HenIT.Data.SqlClient.RaiseMessageDelegate(eventLogDbDAL_RaiseInfoMessage);
                    eventLogDbDAL.RaiseErrorMessage += new HenIT.Data.SqlClient.RaiseMessageDelegate(eventLogDbDAL_RaiseErrorMessage);
                    eventLogDbDAL.RaiseSyncInfoMessage += new HenIT.Data.SqlClient.RaiseMessageDelegate(eventLogDbDAL_RaiseSyncInfoMessage);
                }
                catch (Exception sqlEx)
                {
                    throw new Exception(string.Format("Cannot open database connection!\r\n{0}", sqlEx));
                }
                //checking database version
                LogExtendedMessage("Get database version", EventLogEntryType.Information);
                double dbVersion = eventLogDbDAL.GetDatabaseVersion();
                if (dbVersion < 5.0)
                    throw new Exception("Database version must be 5.0 or later!\r\nCurrent version " + dbVersion.ToString());
                #endregion

                #region Check collector name
                if (CollectorName == null || CollectorName.Length == 0)
                {
                    throw new Exception("Collector name not specified in config file!");
                }
                LogExtendedMessage("Getting Collector settings from DB", EventLogEntryType.Information);
                thisCollector = eventLogDbDAL.GetCollectorByName(CollectorName);
                if (thisCollector == null)
                {
                    throw new Exception("The collector name is not set up in the database!");
                }
                else
                {
                    //for all further sql timeouts set from DB
                    eventLogDbDAL.CommandTimeout = thisCollector.SqlCommandTimeOutSec;
                }
                #endregion
            }
            catch (Exception ex)
            {
                Log.WriteEntry(string.Format("Error setting up database connection or checking collector name:{0}", ex.ToString()), EventLogEntryType.Error);
                throw new Exception(string.Format("Error setting up database connection or checking collector name:{0}", ex.ToString()));
            }

            try
            {
                LogExtendedMessage("Run:Attempting to start main thread", EventLogEntryType.Information);
                new Thread(delegate()
                {
                    RunMainThread();
                }
                ).Start();
            }
            catch (Exception ex)
            {
                Log.WriteEntry(string.Format("Collector {0} starting error:{1}", CollectorName, ex.ToString()), EventLogEntryType.Error);
                throw new Exception(string.Format("Collector {0} starting error:{1}", CollectorName, ex.ToString()));
            }
        }
        #endregion

        #region Methods
        private void RunMainThread()
        {
            int mainThreadFrequencySec = 60;
            System.Threading.Thread.GetDomain().UnhandledException += new UnhandledExceptionEventHandler(MainReaperService_UnhandledException);
            //System.Threading.Thread.GetDomain().FirstChanceException += new EventHandler<System.Runtime.ExceptionServices.FirstChanceExceptionEventArgs>(MainReaperService_FirstChanceException);
            try
            {
                LogExtendedMessage("Run:Attempting to reset 'totals' performance counters", EventLogEntryType.Information);
                SetPerformanceCounterRawValue(machineLogTotalCount, 0);
                SetPerformanceCounterRawValue(machineLogEnabledCount, 0);
                LogExtendedMessage("Run:Done resetting 'totals' performance counters", EventLogEntryType.Information);
            }
            catch (Exception perfEx)
            {
                Log.WriteEntry(string.Format("Error resetting performance counters!\r\nCollector:{0}\r\n{1}", CollectorName, perfEx.ToString()), EventLogEntryType.Error);
            }

            LogExtendedMessage("Starting main thread 'Loop'. Current state of 'Running:" + Running.ToString(), EventLogEntryType.Information);
            if (UserName != null && UserName.Length > 0)
            {
                LogExtendedMessage("Connection to SQL server with the user " + UserName, EventLogEntryType.Information);                                
            }
            LogExtendedMessage("Connection string " + eventLogDbDAL.ConnectionString, EventLogEntryType.Information);
            while (Running)
            {
                try
                {
                    //Check database connection is up
                    //if not then wait until it becomes available or Running = false
                    LogExtendedMessage("Checking for valid DB connectrion", EventLogEntryType.Information);
                    if (CheckForValidDBConnection())
                    {
                        //refresh collector settings
                        LogExtendedMessage("Getting Collector settings from DB", EventLogEntryType.Information);
                        thisCollector = eventLogDbDAL.GetCollectorByName(CollectorName);
                        if (thisCollector != null)
                            mainThreadFrequencySec = thisCollector.MainThreadUpdateFrequencySec;

                        //Check if this collector is responsible for maintenance running
                        //The check if maintenance scheduled time has arrived
                        //only then run maintenance thread.
                        // Run create history as well if enabled
                        //This is done all syncronously so there is less of a chance of importing logs impacting the maintenance routines
                        LogExtendedMessage("Check if this collector is set up for maintenance", EventLogEntryType.Information);
                        if (CollectorName == eventLogDbDAL.GetMaintenanceRunner())
                        {
                            LogExtendedMessage("Running maintenance routines", EventLogEntryType.Information);
                            DoMaintenanceStuff();
                        }

                        //Get EventLogs that must be serviced by this collector
                        //Check each Eventlog/MachineLog for enabled + due check date/time
                        try
                        {
                            LogExtendedMessage("Running UpdateReaperJobs", EventLogEntryType.Information);
                            UpdateReaperJobs();
                        }
                        catch (Exception updateEx)
                        {                            
                            LogExtendedMessage("Error running UpdateReaperJobs.\r\n" + updateEx.ToString(), EventLogEntryType.Error);
                        }
                        try
                        {
                            LogExtendedMessage("Running RunReaperJobs", EventLogEntryType.Information);
                            RunReaperJobs();
                        }
                        catch (Exception runEx)
                        {
                            LogExtendedMessage("Error running RunReaperJobs.\r\n" + runEx.ToString(), EventLogEntryType.Error);
                        }

                        try
                        {
                            //Check if this collector is configured to import any foreign logs in other Event scavenger DBs
                            LogExtendedMessage("Check if this collector is set up for foreign log imports", EventLogEntryType.Information);
                            if (thisCollector != null && thisCollector.ImportsEnabled)
                            {
                                if (lastImportLogDtTm.AddMinutes(thisCollector.ImportFrequency) < DateTime.Now)
                                {
                                    LogExtendedMessage("Run foreign log imports", EventLogEntryType.Information);
                                    RunImportForeignLogs();
                                    lastImportLogDtTm = DateTime.Now;
                                }
                            }
                        }
                        catch (Exception importEx)
                        {
                            LogExtendedMessage("Error running RunImportForeignLogs.\r\n" + importEx.ToString(), EventLogEntryType.Error);
                        }

                        //and now wait until next cycle
                        LogExtendedMessage("Waiting for next cycle...", EventLogEntryType.Information);
                        WaitForTimeOrExit(mainThreadFrequencySec * 1000, waitingTimeDecrement);
                    }
                }
                catch (Exception mainEx)
                {
                    Log.WriteEntry(mainEx.ToString(), EventLogEntryType.Error, (int)EventIDCodes.GeneralError);
                    WaitForTimeOrExit(60000, waitingTimeDecrement); //wait before trying again
                }
            }
#if DEBUG
            Log.WriteEntry("Run:Attempting to reset 'totals' performance counters", EventLogEntryType.Information);
#endif
            SetPerformanceCounterRawValue(machineLogTotalCount, 0);
            SetPerformanceCounterRawValue(machineLogEnabledCount, 0);
#if DEBUG
            Log.WriteEntry("Run:Done resetting 'totals' performance counters", EventLogEntryType.Information);
#endif
            ClosePerformanceCounters();
#if DEBUG
            Log.WriteEntry(string.Format("Main thread stopped\r\nCollector:{0}", CollectorName), EventLogEntryType.Information);
#endif            
        }

        private void UpdateReaperJobs()
        {
            string lastError = "Running UpdateReaperJobs";
            //any kind of updating of the "reaperJobs" list must be locked to only one process at a time.
            try
            {
                lastError = "Setting updating mutex";
                reaperJobUpdatingMutex.WaitOne();
                lastError = "Resetting enabled state on all jobs";
                foreach (ReaperJob j in reaperJobs)
                {
                    j.EnabledToRun = false;
                }
                lastError = "Getting the list of Machine/Logs";
                LogExtendedMessage("Calling GetMachineLogListByCollector as collector -" + CollectorName, EventLogEntryType.Information);
                List<MachineLogEntry> eventLogsToReap = eventLogDbDAL.GetMachineLogListByCollector(CollectorName);
                if (eventLogsToReap!= null)
                    LogExtendedMessage("Machine/log count - " + eventLogsToReap.Count.ToString(), EventLogEntryType.Information);
                else
                    LogExtendedMessage("Machine/log count - None!", EventLogEntryType.Information);
                lastError = "Setting performance counter values";
                SetPerformanceCounterRawValue(machineLogTotalCount, eventLogsToReap.LongCount());
                SetPerformanceCounterRawValue(machineLogEnabledCount, eventLogsToReap.LongCount(el => el.Enabled));
                lastError = "Looping through jobs";
                foreach (MachineLogEntry mle in eventLogsToReap)
                {
                    lastError = "Getting the matching existing job";
                    ReaperJob job = (from j in reaperJobs
                                     where j.Name == mle.ToString()
                                     select j).FirstOrDefault();

                    if (job == null)
                    {
                        lastError = "No matching existing job found";
                        job = new ReaperJob()
                        {
                            Name = mle.ToString(),
                            MLEDetails = mle,
                            EnabledToRun = mle.Enabled,
                            LastComletedDate = new DateTime(2000, 1, 1)
                        };
                        reaperJobs.Add(job);
                    }
                    else
                    {
                        lastError = "Updating existing job";
                        job.MLEDetails = null; //throw away old reference
                        job.MLEDetails = mle;
                        job.EnabledToRun = mle.Enabled;
                    }
                }
            }
            catch (Exception ex)
            {
                LogError(ex.ToString(), EventIDCodes.GeneralError, lastError, "N/A");
            }
            finally
            {
                try
                {
                    reaperJobUpdatingMutex.ReleaseMutex();
                }
                catch { }
            }
            
        }
        private void RunReaperJobs()
        {
            string lastError = "Running RunReaperJobs";
            try
            {
                foreach (ReaperJob j in reaperJobs)
                {
                    try
                    {
                        lastError = "Checking for enabled state for " + j.Name;
                        if (j.EnabledToRun && (j.MLEDetails.LastPollTime.AddMinutes(j.MLEDetails.PollFrequency) < DateTime.Now))
                        {
                            lastError = "Checking if workthread exists already for " + j.Name;
                            if (j.WorkThread == null)
                            {
                                lastError = "Creating new workthread for " + j.Name;
                                Thread newJobThread = new Thread(ReapEventLog);
                                newJobThread.Name = j.Name;
                                j.WorkThread = newJobThread;                                
                                newJobThread.Start(j);
                                j.LastStartDate = DateTime.Now;
                            }
                            else if (j.WorkThread.ThreadState == System.Threading.ThreadState.Stopped) //old one is done
                            {
                                lastError = "Recreating new workthread for " + j.Name;
                                j.WorkThread = null;
                                Thread newJobThread = new Thread(ReapEventLog);
                                newJobThread.Name = j.Name;
                                j.WorkThread = newJobThread;
                                newJobThread.Start(j);
                                j.LastStartDate = DateTime.Now;
                            }
                        }
                    }
                    catch (Exception ex)
                    {
                        Log.WriteEntry("RunReaperJobs:" + ex.ToString() + "\r\nLast step:" + lastError, EventLogEntryType.Error, (int)EventIDCodes.StartThread);
                    }
                }
            }
            catch (Exception runReaperJobsEx)
            {
                Log.WriteEntry("RunReaperJobs:" + runReaperJobsEx.ToString() + "\r\nLast step:" + lastError, EventLogEntryType.Error, (int)EventIDCodes.GeneralError);
            }
        }
        private void DoMaintenanceStuff()
        {
            try
            {
                if (eventLogDbDAL.IsMaintenanceEnabled())
                {
                    int maintenanceTimeOutSeconds = eventLogDbDAL.GetMaintenanceTimeOutSeconds();
                    int maintenanceFrequencyMinutes = eventLogDbDAL.GetMaintenanceFrequencyMinutes();
                    DateTime lastRun = eventLogDbDAL.GetMaintenanceLastRunTime();
                    if (lastRun.AddMinutes(maintenanceFrequencyMinutes) < DateTime.Now)
                    {
                        Log.WriteEntry(string.Format("Running maintenance now for database {0}\\{1} from collector {2}", eventLogDbDAL.Server, eventLogDbDAL.Database, CollectorName), EventLogEntryType.Information, (int)EventIDCodes.Maintenance);
                        eventLogDbDAL.RunMaintenance(maintenanceTimeOutSeconds);
                        eventLogDbDAL.SetMaintenanceLastRunTime();
                        Log.WriteEntry(string.Format("Maintenance done for database {0}\\{1} from collector {2}", eventLogDbDAL.Server, eventLogDbDAL.Database, CollectorName), EventLogEntryType.Information, (int)EventIDCodes.Maintenance);
                    }
                }
                if (eventLogDbDAL.IsRunCreateHistoryEnabled())
                {
                    DateTime nextRun = eventLogDbDAL.GetNextCreateHistoryTime();
                    if (nextRun < DateTime.Now)
                    {
                        Log.WriteEntry(string.Format("Running create history now for database {0}\\{1} from collector {2}", eventLogDbDAL.Server, eventLogDbDAL.Database, CollectorName), EventLogEntryType.Information, (int)EventIDCodes.Maintenance);
                        eventLogDbDAL.RunCreateHistory();
                        eventLogDbDAL.SetNextCreateHistoryTime();
                        Log.WriteEntry(string.Format("Create history done for database {0}\\{1} from collector {2}", eventLogDbDAL.Server, eventLogDbDAL.Database, CollectorName), EventLogEntryType.Information, (int)EventIDCodes.Maintenance);
                    }
                }
            }
            catch (Exception ex)
            {
                Log.WriteEntry(string.Format("Error(s) occured white performing maintenance routines. Collector {0}\r\n{1}", CollectorName, ex), EventLogEntryType.Error, (int)EventIDCodes.Maintenance);
            }
        }
        private void RunImportForeignLogs()
        {
            if (importLogsThread == null || importLogsThread.ThreadState == System.Threading.ThreadState.Stopped)
            {
                importLogsThread = null;
                importLogsThread = new Thread(ReapForeignLog);
                importLogsThread.Start();
            }
        }
        private bool CheckForValidDBConnection()
        {
            bool success = true;
            bool waitingOnError = false;
            while (Running && (!eventLogDbDAL.TestConnection()))
            {
                waitingOnError = true;
                Log.WriteEntry(string.Format("SQL server connection failed. This could be because SQL server is not started or not available yet. Waiting 1 minute to retry connection. Collector:{0}", CollectorName), EventLogEntryType.Warning, (int)EventIDCodes.ConnectionError);
                WaitForTimeOrExit(60000, waitingTimeDecrement); //wait 1 minute and try again
            }
            if (waitingOnError)
                Log.WriteEntry(string.Format("SQL server connection restored. Continuing...\r\nCollector:{0}", CollectorName), EventLogEntryType.Information, (int)EventIDCodes.DatabaseError);

            return success;
        }
        private void WaitForTimeOrExit(int nextWaitInterval, int decrementBy)
        {
            int waitTimeRemaining;
            if (!Running)
                return;
            try
            {
                if ((nextWaitInterval <= decrementBy) && (nextWaitInterval > 0))
                {
                    Thread.Sleep(nextWaitInterval);
                }
                else
                {
                    waitTimeRemaining = nextWaitInterval;
                    while (Running && (waitTimeRemaining > 0))
                    {
                        if (waitTimeRemaining <= decrementBy)
                        {
                            waitTimeRemaining = 0;
                        }
                        else
                        {
                            waitTimeRemaining -= decrementBy;
                        }
                        if (decrementBy > 0)
                        {
                            Thread.Sleep(decrementBy);
                        }
                        if (Paused)
                            SetPerformanceCounterRawValue(machineLogEnabledCount, 0);
                    }
                }
                while (Paused && Running)
                {
                    SetPerformanceCounterRawValue(machineLogEnabledCount, 0);
                    Thread.Sleep(decrementBy);
                }
            }
            catch (ThreadAbortException excabort)
            {
                Log.WriteEntry(string.Format("Error in waiting\r\n{0}", excabort));
                Thread.Sleep(nextWaitInterval);
            }
        }
        #endregion

        #region ReapEventLog
        private void ReapEventLog(object data)
        {
            ReaperJob jobDetails;
            if (data is ReaperJob)
                jobDetails = (ReaperJob)data;
            else
            {
                LogError("Unable to read thread initualization data!", EventIDCodes.SystemEvents, "", "N/A");
                return;
            }           

            List<EventLogEntryEx> eventBufferList;
            string lastStep = "ReapEventLog";
            try
            {                
                lastStep = "Polling Eventlog of " + jobDetails.Name;
                if (jobDetails.MLEDetails != null)
                {
                    lastStep = "Check if network is up";
                    if (!PingHelper.IsOffline()) //only bother if we're online
                    {
                        //if the host was offline before report that is available again.
                        if (jobDetails.MLEDetails.HostOffline)
                        {
                            Log.WriteEntry("The host/machine " + jobDetails.MLEDetails.Machine + " is available again", EventLogEntryType.Information, (int)EventIDCodes.Network);
                        }
                        jobDetails.MLEDetails.HostOffline = false;

                        #region Poll Eventlog and write to database
                        lastStep = "Retrieve eventlog";
                        eventBufferList = GetNewEventLogEntries(jobDetails.MLEDetails);
                        lastStep = "Writing Eventlog entries to database";
                        if (InsertEntriesIntoDB(jobDetails.MLEDetails, eventBufferList))
                        {
                            if (Running)
                            {
                                DateTime lastEntryDateTime;
                                lastStep = "Writing last polling time";
                                jobDetails.LastComletedDate = DateTime.Now;
                                if (eventBufferList != null && eventBufferList.Count > 0)
                                {
                                    lastEntryDateTime = eventBufferList.Max(el => el.TimeGenerated);
                                }
                                else //if there were no events matching the filter during that time then set to time started polling
                                    //otherwise the service has to struggle through all the filtered events again.
                                    lastEntryDateTime = DateTime.Now;
                                eventLogDbDAL.SetLastMachineLogPollingTime(jobDetails.MLEDetails.Machine, jobDetails.MLEDetails.LogName, lastEntryDateTime);
                            }
                        }
                        #endregion
                    }
                    else if (!jobDetails.MLEDetails.HostOffline)
                    {
                        jobDetails.MLEDetails.HostOffline = true;
                        lastStep = "Retrieve reason ping failed";
                        string reason = PingHelper.GetPingHostErrorReason(jobDetails.MLEDetails.Machine);
                        LogError("The host/machine " + jobDetails.MLEDetails.Machine + " is not available!\r\nReason:" + reason, EventIDCodes.Network, lastStep, jobDetails.Name);
                    }
                }
                else
                {
                    LogError("jobDetails.MLEDetails is NULL!\r\n" + jobDetails.ToString(), EventIDCodes.GeneralError, lastStep, "N/A");
                }
            }
            catch (System.Data.SqlClient.SqlException sqlex)
            {
                LogError(sqlex.ToString(), EventIDCodes.DatabaseError, lastStep, jobDetails.Name);
            }
            catch (System.ComponentModel.Win32Exception win32ex)
            {
                LogError(win32ex.ToString(), EventIDCodes.RemoteProcedure, lastStep, jobDetails.Name);
            }
            catch (System.InvalidOperationException invalex)
            {
                LogError(invalex.ToString(), EventIDCodes.InvalidOperation, lastStep, jobDetails.Name);
            }
            catch (System.IO.IOException ioex)
            {
                LogError(ioex.ToString(), EventIDCodes.IOError, lastStep, jobDetails.Name);
            }
            catch (System.Security.SecurityException scex)
            {
                LogError(scex.ToString(), EventIDCodes.SecurityError, lastStep, jobDetails.Name);
            }
            catch (Exception ex) //reporting all other exceptions within this thread
            {
                LogError("General error " + jobDetails.Name + "\r\n" + ex.ToString() + "\r\n" + ex.GetType().ToString(), EventIDCodes.GeneralError, lastStep, jobDetails.Name);
            }
        }
        private void ReapForeignLog(object data)
        {
            string lastStep = "Run Imports";
            try
            {
                lastStep = "Get list of logs to import";
                foreach (ImportForeignLog importlog in eventLogDbDAL.SelectImportLogsByCollector(thisCollector.Id))
                {
                    try
                    {
                        lastStep = "Getting log details for - " + importlog.ToString();
                        MachineLogEntry logToImport = eventLogDbDAL.GetMachineLogEntry(importlog.Machine, importlog.LogName);
                        int lastRecordNumber = 0;
                        lastStep = "Import - " + importlog.ToString();
                        while (Running && !Paused)
                        {
                            lastStep = "Getting import entries from - " + importlog.ToString();
                            List<EventLogEntryEx> entryList = eventLogDbDAL.GetImportLogEntries(importlog.SQLServer, importlog.Database, logToImport, thisCollector.InsertBatchSize, lastRecordNumber);

                            //bug when there are more than thisCollector.InsertBatchSize entries on the same second the batch routine will get stuck
                            if (entryList.Count > 1)
                            {
                                DateTime oldest = entryList.Min(entr => entr.TimeGenerated);
                                DateTime newest = entryList.Max(entr => entr.TimeGenerated);

                                if (oldest.ToString("yyyy-MM-dd HH:mm:ss") == newest.ToString("yyyy-MM-dd HH:mm:ss"))
                                {
                                    lastRecordNumber = entryList.Max(entr => entr.Index);
                                }
                            }

                            lastStep = "Importing entries from - " + importlog.ToString();
                            InsertEntriesIntoDB(logToImport, entryList);
                            lastStep = "Setting last poll date time for - " + importlog.ToString();
                            if (entryList.Count > 0)
                            {
                                IncrementCounter(entriesImportedPerSecond, entryList.Count);
                                logToImport.LastPollTime = entryList.Max(el => el.TimeGenerated);
                            }
                            else
                                logToImport.LastPollTime = DateTime.Now;
                            if (entryList.Count < thisCollector.InsertBatchSize)
                                break;
                        }
                    }
                    catch (System.Data.SqlClient.SqlException sqlex)
                    {
                        LogError(sqlex.ToString(), EventIDCodes.DataImport, lastStep, "Import logs");
                    }
                } 
            }
            catch (Exception ex)
            {
                LogError(ex.ToString() + "\r\n" + ex.GetType().ToString(), EventIDCodes.DataImport, lastStep, "Import logs");
            }            
        }
        private bool InsertEntriesIntoDB(MachineLogEntry mle, List<EventLogEntryEx> eventBufferList)
        {
            bool success = false;
            string lastStep = "Inserting event log entry batches";
            int connectionErrors = 0;
            int batchTimeUpdaterCounter = 0;
            int batchSize = 100;
            Stopwatch sw = new Stopwatch();
            try
            {
                if (thisCollector.UseBatchInserts)
                {
                    #region Batch inserts                    
                    List<EventLogEntryEx> batch = null;
                    batchSize = thisCollector.InsertBatchSize;
                    while (eventBufferList.Count > batchSize)
                    {
                        lastStep = "Getting new batch of event log entries";
                        sw.Restart();
                        batch = eventBufferList.Take(batchSize).ToList();
                        sw.Stop();
                        TraceDuration("InsertEntriesIntoDB", "Getting new batch of event log entries", sw.Elapsed.TotalSeconds, mle.ToString());

                        lastStep = "Inserting batch of event log entries";
                        sw.Restart();
                        XmlBatchInsert(mle, batch);
                        sw.Stop();
                        TraceDuration("InsertEntriesIntoDB", "Inserting batch of event log entries", sw.Elapsed.TotalSeconds, mle.ToString());

                        lastStep = "Getting newest entry time from batch";
                        sw.Restart();
                        mle.NewestEntry = batch.Max(en => en.TimeGenerated);
                        sw.Stop();
                        TraceDuration("InsertEntriesIntoDB", "Getting newest entry time from batch", sw.Elapsed.TotalSeconds, mle.ToString());

                        lastStep = "Removing batch of event log entries";
                        sw.Restart();
                        eventBufferList.RemoveRange(0, batchSize);
                        sw.Stop();
                        TraceDuration("InsertEntriesIntoDB", "Removing batch of event log entries", sw.Elapsed.TotalSeconds, mle.ToString());

                        lastStep = "Writing last polling time";
                        sw.Restart();
                        eventLogDbDAL.SetLastMachineLogPollingTime(mle.Machine, mle.LogName, mle.NewestEntry);
                        sw.Stop();
                        TraceDuration("InsertEntriesIntoDB", "Writing last polling time", sw.Elapsed.TotalSeconds, mle.ToString());

                        if (!Running || Paused)
                        {
                            return true;
                        }
                    }
                    if (eventBufferList.Count > 0)
                    {
                        lastStep = "Inserting batch of event log entries";
                        sw.Restart();
                        XmlBatchInsert(mle, eventBufferList);
                        sw.Stop();
                        TraceDuration("InsertEntriesIntoDB", "Inserting batch of event log entries", sw.Elapsed.TotalSeconds, mle.ToString());

                        lastStep = "Getting newest entry time from batch";
                        sw.Restart();
                        mle.NewestEntry = eventBufferList.Max(en => en.TimeGenerated);
                        sw.Stop();
                        TraceDuration("InsertEntriesIntoDB", "Getting newest entry time from batch", sw.Elapsed.TotalSeconds, mle.ToString());

                        lastStep = "Writing last polling time";
                        sw.Restart();
                        eventLogDbDAL.SetLastMachineLogPollingTime(mle.Machine, mle.LogName, mle.NewestEntry);
                        sw.Stop();
                        TraceDuration("InsertEntriesIntoDB", "Writing last polling time", sw.Elapsed.TotalSeconds, mle.ToString());
                    } 
                    #endregion
                }
                else
                {
                    #region Non batch inserts
                    sw.Restart();
                    foreach (EventLogEntryEx entry2Read in eventBufferList)
                    {
                        batchTimeUpdaterCounter++;
                        lastStep = "Reading Event ID";
                        int eventId = (int)(entry2Read.InstanceId & 65535);
                        lastStep = "Attempting to write entry to database";
                        try
                        {
                            string strings = "";
                            try
                            {
                                if (entry2Read.Data.Length > 0)
                                {
                                    strings = Encoding.UTF8.GetString(entry2Read.Data, 0, entry2Read.Data.Length);
                                    if (strings != null && strings.Length > 0 && strings.Contains('�'))
                                        strings = strings.Replace('�', '?');
                                }
                            }
                            catch { }
                            eventLogDbDAL.InsertEventLogEntry(
                                mle.Machine,
                                mle.LogName,
                                entry2Read.Index,
                                eventId,
                                entry2Read.TimeGenerated,
                                entry2Read.Source,
                                entry2Read.EntryType,
                                strings,
                                entry2Read.Message,
                                entry2Read.Category,
                                entry2Read.UserName);
                            connectionErrors = 0;
                        }
                        catch (System.ArgumentOutOfRangeException)
                        {
                            IncrementCounter(errorsPerSecond); //inc perf counter but continue
                        }
                        catch (System.InvalidOperationException ioEx)
                        {
                            IncrementCounter(errorsPerSecond);
                            if (ioEx.Message.Contains("Internal connection fatal error."))
                            {
                                connectionErrors++;
                                if (connectionErrors > 2) //3 retries
                                    throw;
                            }
                            else
                                throw;
                        }
                        catch
                        {
                            IncrementCounter(errorsPerSecond);
                            throw;
                        }

                        //Set last entry datetime to current entry in case service stops/fails etc. to continue from this point if restarted
                        mle.NewestEntry = entry2Read.TimeGenerated;

                        //if batchsize is reached update machinelog
                        if (batchTimeUpdaterCounter > batchSize)
                        {
                            batchTimeUpdaterCounter = 0;
                            lastStep = "Writing last polling time";
                            eventLogDbDAL.SetLastMachineLogPollingTime(mle.Machine, mle.LogName, mle.NewestEntry);
                        }
                        //if the service is stopping, paused or recycling stop processing
                        if (!Running || Paused)
                            break;
                    }
                    sw.Stop();
                    TraceDuration("InsertEntriesIntoDB", "Non batch option", sw.Elapsed.TotalSeconds, mle.ToString());                         
                    #endregion
                }
                success = true;
            }
            catch (Exception ex)
            {
                throw new Exception(string.Format("InsertEntriesIntoDB:{0}", lastStep), ex);
            }
            return success;
        }
        private void XmlBatchInsert(MachineLogEntry mle, List<EventLogEntryEx> batch)
        {
            Stopwatch sw = new Stopwatch();
            Stopwatch sw2 = new Stopwatch();
            string message;
            try
            {
                System.IO.MemoryStream outstream = new System.IO.MemoryStream();
                using (System.Xml.XmlTextWriter wrt = new System.Xml.XmlTextWriter(outstream, Encoding.UTF8))
                {
                    wrt.WriteStartDocument();
                    wrt.WriteStartElement("rs");

                    foreach (EventLogEntryEx ele in batch)
                    {
                        
                        wrt.WriteStartElement("r");
                        wrt.WriteAttributeString("cn", mle.Machine);
                        wrt.WriteAttributeString("l", mle.LogName);
                        wrt.WriteAttributeString("r", ele.Index.ToString());
                        wrt.WriteAttributeString("e", ele.InstanceId.ToString());
                        wrt.WriteAttributeString("t", ele.TimeGenerated.ToString("yyyy-MM-ddTHH:mm:ss"));
                        wrt.WriteAttributeString("s", ele.Source);
                        wrt.WriteAttributeString("et", ele.EntryType);
                        wrt.WriteAttributeString("c", N(ele.Category));                                               
                        wrt.WriteAttributeString("u", N(ele.UserName));

                        sw2.Start();
                        message = ele.Message;
                        sw2.Stop();
                        sw.Start();
                        try
                        {
                            if (message.Length == 0)
                            {
                                string strings = Encoding.UTF8.GetString(ele.Data, 0, ele.Data.Length);
                                if (strings != null && strings.Length > 0)
                                    if (!strings.Contains('�'))
                                        message = strings;
                            }

                        }
                        catch(Exception dataEx) 
                        {
                            Trace.WriteLine("XmlBatchInsert:Data stuff" + dataEx.Message);
                        }
                        try
                        {
                            if (message.IndexOf("\r\n") > -1)
                                message = message.Replace("\r\n", "\\r\\n");
                            if (message.IndexOf('') > -1) //&#x1C; not allowed in XML
                                message = message.Replace('', ' ');

                            if (message.IndexOf('') > -1)
                                message = message.Replace('', ' ');
                            if (message.IndexOf('\0') > -1)
                                message = message.Replace('\0', '.');
                            if (message.IndexOf('') > -1)
                                message = message.Replace('', ' ');

                            if (!IsValidXmlString(message))
                                message = RemoveInvalidXmlChars(message);
                        }
                        catch(Exception invalidMsgXml)
                        {
                            Trace.WriteLine("Invalid Msg XML:" + invalidMsgXml.Message);
                        }
                        sw.Stop(); 

                        wrt.WriteAttributeString("m", message);
                        wrt.WriteEndElement(); //r

                        
                        if (!Running || Paused)
                            break;
                    }

                    wrt.WriteEndElement(); //rs
                    wrt.WriteEndDocument();
                    wrt.Flush();

                    outstream.Position = 0;
                    string infoResult = eventLogDbDAL.InsertEventLogEntries(outstream);
                    if (infoResult.Length > 0 && thisCollector.LogDuplicateWarnings)
                    {
                        Log.WriteEntry("Not all entries could be inserted into the database!\r\n" + infoResult +
                            "\r\nFor '" + mle.Machine + "\\" + mle.LogName + "'", EventLogEntryType.Warning,
                            (int)EventIDCodes.RepeatError);
                    }
                }
                TraceDuration("XmlBatchInsert", "Total duration part1", sw.Elapsed.TotalSeconds, "");
                TraceDuration("XmlBatchInsert", "Total duration part2", sw2.Elapsed.TotalSeconds, "");
            }
            catch (Exception ex)
            {
                Trace.WriteLine(ex.ToString());
                IncrementCounter(errorsPerSecond);
            }
        }
        private static bool IsValidXmlString(string text)
        {
            try
            {
                byte[] bytes = System.Text.Encoding.UTF8.GetBytes(text);
                foreach(byte b in bytes){
                    if (b < 32)
                        return false;
                }
                //System.Text.UTF8Encoding.Convert(Encoding.UTF8, Encoding.ASCII,  text.ToCharArray()
                System.Xml.XmlConvert.VerifyXmlChars(text);
                return true;
            }
            catch
            {
                return false;
            }
        }
        private static string RemoveInvalidXmlChars(string text)
        {
            var validXmlChars = text.Where(ch => System.Xml.XmlConvert.IsXmlChar(ch)).ToArray();
            return new string(validXmlChars);
        }
        private List<EventLogEntryEx> GetNewEventLogEntries(MachineLogEntry machineLogEntry)
        {
            string lastStep = "Read Eventlog entries of " + machineLogEntry.ToString();
            int indexErr = 0;
            EventLogEntry entry2Read = null;
            List<EventLogEntryEx> eventBufferList = new List<EventLogEntryEx>();
            Stopwatch sw = new Stopwatch();
            try
            {
                lastStep = "Check if machine is available";                
                if (!PingHelper.CanPingHost(machineLogEntry.Machine))
                {
                    lastStep = "Try using full DNS name of machine";
                    string fullDNSName = System.Net.Dns.GetHostEntry(machineLogEntry.Machine).HostName;
                    if (!PingHelper.CanPingHost(fullDNSName))
                    {
                        LogError(string.Format("The machine {0} cannot be accessed or does not exist!", machineLogEntry.Machine), EventIDCodes.ReadEventLogError, lastStep, machineLogEntry.ToString());
                        return eventBufferList;
                    }
                    else
                        machineLogEntry.Machine = fullDNSName;
                }
                lastStep = "Check if event log exists";  
                if (!EventLog.Exists(machineLogEntry.LogName, machineLogEntry.Machine))
                {
                    LogError(string.Format("The event log {0}\\{1} cannot be opened or does not exist!", machineLogEntry.Machine, machineLogEntry.LogName), EventIDCodes.ReadEventLogError, lastStep, machineLogEntry.ToString());
                }
                else
                {
                    using (EventLog log2Read = new EventLog(machineLogEntry.LogName, machineLogEntry.Machine))
                    {
                        lastStep = "Getting index of oldest entry";
                        int index = log2Read.Entries.Count - 1;
                        //the following loop is to eliminate the event where entries have been 
                        //truncated since the eventlog has been opened causing the 
                        //'Index x is out of bounds' error
                        #region Get biggest index not causing an 'Index out of bounds' error.
                        sw.Restart();
                        while (index > -1)
                        {
                            try
                            {
                                entry2Read = log2Read.Entries[index];
                                break;
                            }
                            catch (Exception aex)
                            {
                                if (aex.Message.Contains("is out of bounds")) //like in Index xxx is out of bounds.
                                {
                                    LogExtendedMessage(string.Format("Index is out of bounds error from '{0}'", machineLogEntry.ToString()), EventLogEntryType.Warning);
                                    index--;
                                    if (!Running || Paused)
                                    {
                                        return new List<EventLogEntryEx>();
                                    }
                                }
                                else if (aex.Message.Contains("The event log may be corrupt")) //like in Index xxx is out of bounds.
                                {
                                    LogError(string.Format("The event log may be corrupt.", machineLogEntry.ToString()), EventIDCodes.ReadEventLogError, lastStep, machineLogEntry.ToString());
                                    return new List<EventLogEntryEx>();
                                }
                                else
                                {
                                    LogError(aex.ToString(), EventIDCodes.ReadEventLogError, lastStep, machineLogEntry.ToString());
                                    return new List<EventLogEntryEx>();
                                }
                            }
                        }
                        sw.Stop();
                        TraceDuration("GetNewEventLogEntries", "Get oldset index", sw.Elapsed.TotalSeconds, "");
                        #endregion

                        sw.Restart();
                        for (int i = index; i >= 0; i--)
                        {
                            try
                            {
                                lastStep = "Reading entry " + i.ToString();
                                entry2Read = log2Read.Entries[i];
                                lastStep = "Check if it is newer than existing entries";
                                if (entry2Read.TimeGenerated > machineLogEntry.LastPollTime)
                                {
                                    if (machineLogEntry.PollFilterID == 0)
                                    {
                                        eventBufferList.Insert(0, EventLogEntryEx.FromEventLogEntry(entry2Read));
                                        IncrementCounter(eventEntriesPerSecond);
                                    }
                                    else
                                    {
                                        //Accessing some properties of the EventLogEntry object for remote machines can be very slow
                                        //therefore first check the Entry Type before creating a new EventLogEntryEx instance
                                        if (machineLogEntry.PollFilter != null && machineLogEntry.PollFilter.IsEventLogEntryTypeInFilter(entry2Read.EntryType))
                                        {
                                            EventLogEntryEx eventLogEntryEx = EventLogEntryEx.FromEventLogEntry(entry2Read);
                                            if (
                                                    (!machineLogEntry.PollFilter.UseListFilters) || //don't bother if there are no lists/text
                                                    machineLogEntry.PollFilter.IsInFilter(eventLogEntryEx.Message, eventLogEntryEx.InstanceId, entry2Read.Source)
                                                )
                                            {
                                                eventBufferList.Insert(0, EventLogEntryEx.FromEventLogEntry(entry2Read));
                                                IncrementCounter(eventEntriesPerSecond);
                                            }
                                        }
                                    }
                                }
                                else
                                {
                                    break;
                                }
                                indexErr = 0;
                            }
                            catch (Exception aex)
                            {
                                if (aex.Message.Contains("is out of bounds")) //like in Index xxx is out of bounds.
                                {
                                    LogExtendedMessage(string.Format("Index is out of bounds error from '{0}'", machineLogEntry.ToString()), EventLogEntryType.Warning);
                                    indexErr++;
                                    if (indexErr > 10)
                                    {
                                        LogError(aex.Message, EventIDCodes.ReadEventLogError, lastStep, machineLogEntry.ToString());
                                        return new List<EventLogEntryEx>();
                                    }
                                }
                                else
                                {
                                    LogError(aex.ToString(), EventIDCodes.ReadEventLogError, lastStep, machineLogEntry.ToString());
                                    return new List<EventLogEntryEx>();
                                }
                            }
                            lastStep = "Check if thread is still running";
                            //if the service is stopping, paused or recycling stop processing
                            if (!Running || Paused)
                            {
                                eventBufferList = new List<EventLogEntryEx>();
                                break;
                            }
                        }
                        sw.Stop();
                        TraceDuration("GetNewEventLogEntries", "Copy all new entries", sw.Elapsed.TotalSeconds, "");
                    }
                }
            }
            catch (System.IO.IOException ioex)
            {
                try
                {
                    if (ioex.Message.Contains("The network path was not found"))
                    {
                        LogError("The network path was not found", EventIDCodes.ReadEventLogError, lastStep, machineLogEntry.ToString());
                    }

                    lastStep = "Trying to connect using full dns name";
                    LogExtendedMessage("IOException - trying to use full DNS name for " + machineLogEntry.Machine, EventLogEntryType.Warning);
                    string fullDNSName = System.Net.Dns.GetHostEntry(machineLogEntry.Machine).HostName;
                    if (fullDNSName.Length > 0 && fullDNSName.ToUpper() != machineLogEntry.Machine.ToUpper())
                    {
                        machineLogEntry.Machine = fullDNSName;
                        eventBufferList = GetNewEventLogEntries(machineLogEntry);
                    }
                    else
                    {
                        LogExtendedMessage("DNS name match so there is probably another error" + machineLogEntry.Machine, EventLogEntryType.Error);
                        IncrementCounter(errorsPerSecond);
                        //throw new Exception(string.Format("The network path was not found (using full dns name)\r\nLast Step:{0}", lastStep));
                        eventBufferList = new List<EventLogEntryEx>();
                        LogError("The network path was not found (using full dns name)", EventIDCodes.ReadEventLogError, lastStep, machineLogEntry.ToString());
                    }
                }
                catch (Exception innerex)
                {
                    LogExtendedMessage("Error handing IOException - " + machineLogEntry.Machine, EventLogEntryType.Warning);
                    IncrementCounter(errorsPerSecond);
                    eventBufferList = new List<EventLogEntryEx>();
                    LogError(string.Format("{0}\r\nType:{1}", innerex.Message, innerex.GetType().ToString()), EventIDCodes.ReadEventLogError, lastStep, machineLogEntry.ToString());
                }
            }
            catch (Exception ex)
            {
                LogExtendedMessage("GetNewEventLogEntries:" + ex.ToString(), EventLogEntryType.Error);
                IncrementCounter(errorsPerSecond);
                eventBufferList = new List<EventLogEntryEx>();
                LogError(ex.Message, EventIDCodes.ReadEventLogError, lastStep, machineLogEntry.ToString());
            }
            return eventBufferList;
        }
        #endregion

        #region Error logging
        private void LogError(string errorMessage, EventIDCodes errorCode, string lastStep, string machineLogName)
        {
            try
            {
                Log.WriteEntry("Machine\\Log:" + machineLogName + "\r\n" +
                               "Last step:" + lastStep + "\r\n" +
                               "Collector:" + CollectorName + "\r\n" + 
                               errorMessage, EventLogEntryType.Error, (int)errorCode);
            }
            catch { } //Ignore further errors here. This is only an attempt for more info.
        }
        private void LogError(string errorMessage, EventIDCodes errorCode, EventLogEntry entry2Read, string lastStep, string machineLogName)
        {
            try
            {
                Log.WriteEntry(lastStep + "\r\n" +
                                   "Machine\\Log:" + machineLogName + "\r\n" +
                                   errorMessage, EventLogEntryType.Error, (int)errorCode);

                if (entry2Read != null)
                {
                    int eventId = (int)(entry2Read.InstanceId & 65535);
                    string lastEntry = "Last step before: " + lastStep +
                            "\r\nLast entry processed\r\nTime:" + entry2Read.TimeGenerated.ToString() +
                            "Machine\\Log:" + machineLogName + "\r\n" +
                            "\r\nSource:" + entry2Read.Source +
                            "\r\nType:" + entry2Read.EntryType.ToString() +
                            "\r\nEventID:" + eventId.ToString() +
                            "\r\nMessage:" + entry2Read.Message;
                    Log.WriteEntry(lastEntry, EventLogEntryType.Warning, (int)EventIDCodes.RepeatError);
                }
            }
            catch { } //Ignore further errors here. This is only an attempt for more info.
        }
        private void LogExtendedMessage(string message, EventLogEntryType logType)
        {
            if (Properties.Settings.Default.EnabledExtendedLogging)
            {
                Log.WriteEntry(message, logType);
            }
        }
        private void LogMessage(string message, EventLogEntryType logType)
        {
            Log.WriteEntry(message, logType);
        }
        private void MainReaperService_FirstChanceException(object sender, System.Runtime.ExceptionServices.FirstChanceExceptionEventArgs e)
        {
            LogMessage(e.Exception.ToString(), EventLogEntryType.Error);
        }
        private void MainReaperService_UnhandledException(object sender, UnhandledExceptionEventArgs e)
        {
            LogMessage(((Exception)e.ExceptionObject).ToString(), EventLogEntryType.Error);
        }
        #endregion

        #region Null handling
        private static string N(object anyVal)
        {
            if (anyVal == null)
                return "";
            else
                return anyVal.ToString();
        }
        #endregion

        #region Performance Counters
        internal void InitPerformanceCounters(string serviceSource)
        {
            try
            {
                if (Properties.Settings.Default.RecreatePerfCountersOnStart)
                {
                    if (PerformanceCounterCategory.Exists(serviceSource))
                    {
                        Log.WriteEntry("Attempting to recreate performance counters for this service.", EventLogEntryType.Information);
                        PerformanceCounterCategory.Delete(serviceSource);
                    }
                }

                if (!PerformanceCounterCategory.Exists(serviceSource))
                {
                    CounterCreationData[] collectorCounterCreationData = new CounterCreationData[]
                    {
                        new CounterCreationData("Total event logs", "Total number of event logs monitored", PerformanceCounterType.NumberOfItems32),
                        new CounterCreationData("Enabled event logs", "Total active number of event logs monitored", PerformanceCounterType.NumberOfItems32),
                        new CounterCreationData("Event entries/Sec", "Event entries read per second", PerformanceCounterType.RateOfCountsPerSecond32),
                        new CounterCreationData("Errors/Sec", "Total errors per second", PerformanceCounterType.RateOfCountsPerSecond32),
                        new CounterCreationData("Database accesses/Sec", "Total database accesses per second", PerformanceCounterType.RateOfCountsPerSecond32),
                        new CounterCreationData("Database inserts/Sec", "Database Inserts per second", PerformanceCounterType.RateOfCountsPerSecond32),
                        new CounterCreationData("Database duplicate inserts/Sec", "Database duplicate inserts per second", PerformanceCounterType.RateOfCountsPerSecond32),
                        new CounterCreationData("Entries imported/Sec", "Event entries imported per second", PerformanceCounterType.RateOfCountsPerSecond32)
                    };
                    PerformanceCounterCategory.Create(serviceSource, "Event Reaper Service", PerformanceCounterCategoryType.MultiInstance, new CounterCreationDataCollection(collectorCounterCreationData));
                } 
                Thread.Sleep(1000);
            }
            catch (Exception ex)
            {
                Log.WriteEntry(string.Format("Error (re)creating performance counter categories.\r\n{0}", ex), EventLogEntryType.Error);
                throw;
            }

            try
            {
                if (CollectorName.Length > 0)
                {
                    machineLogTotalCount = InitializePerfCounterInstance(serviceSource, "Total event logs", CollectorName);
                    machineLogEnabledCount = InitializePerfCounterInstance(serviceSource, "Enabled event logs", CollectorName);
                    eventEntriesPerSecond = InitializePerfCounterInstance(serviceSource, "Event entries/Sec", CollectorName);
                    errorsPerSecond = InitializePerfCounterInstance(serviceSource, "Errors/Sec", CollectorName);
                    dbAccessPerSecond = InitializePerfCounterInstance(serviceSource, "Database accesses/Sec", CollectorName);
                    dbInsertsPerSecond = InitializePerfCounterInstance(serviceSource, "Database inserts/Sec", CollectorName);
                    dbDuplicateInsertsPerSecond = InitializePerfCounterInstance(serviceSource, "Database duplicate inserts/Sec", CollectorName);
                    entriesImportedPerSecond = InitializePerfCounterInstance(serviceSource, "Entries imported/Sec", CollectorName);
                }
            }
            catch (Exception ex)
            {
                Log.WriteEntry(string.Format("Error (re)creating performance counter.\r\n{0}", ex), EventLogEntryType.Error);
                throw;
            }
        }
        internal void ClosePerformanceCounters()
        {
            try
            {
                if (machineLogTotalCount != null)
                {
                    machineLogTotalCount.Close();
                    machineLogTotalCount.Dispose();
                }
                if (machineLogEnabledCount != null)
                {
                    machineLogEnabledCount.Close();
                    machineLogEnabledCount.Dispose();
                }
            }
            catch (Exception ex)
            {
                Log.WriteEntry(string.Format("Error closing performance counters.\r\n{0}", ex), EventLogEntryType.Error);
            }
        }
        private static PerformanceCounter InitializePerfCounterInstance(string categoryName, string counterName)
        {
            PerformanceCounter counter = new PerformanceCounter(categoryName, counterName, false);
            counter.BeginInit();
            counter.RawValue = 0;
            counter.EndInit();
            return counter;
        }
        private static PerformanceCounter InitializePerfCounterInstance(string categoryName, string counterName, string instanceName)
        {
            PerformanceCounter counter = new PerformanceCounter(categoryName, counterName, instanceName, false);
            counter.BeginInit();
            counter.RawValue = 0;
            counter.EndInit();
            return counter;
        }
        private void SetPerformanceCounterRawValue(PerformanceCounter counter, long value)
        {
            try
            {
                if (counter!= null)
                    counter.RawValue = value;
            }
            catch(Exception ex) 
            {
#if DEBUG
                Log.WriteEntry(string.Format("SetPerformanceCounterRawValue Error:{0}", ex), EventLogEntryType.Error);
#endif
                Trace.WriteLine(string.Format("SetPerformanceCounterRawValue Error:{0}", ex));
            }
        }
        private void IncrementCounter(PerformanceCounter counter)
        {
            try
            {
                if (counter == null)
                {
                    LogError("Performance counter not set up or installed! Please verify installation!", EventIDCodes.SystemEvents, "Performance counter is not set up", "N/A");
                }
                else
                {
                    counter.Increment();
                }
            }
            catch (Exception ex)
            {
                LogError(ex.ToString(), EventIDCodes.SystemEvents, "Increment performance counter", counter.MachineName);
            }
        }
        private void IncrementCounter(PerformanceCounter counter, long incrementBy)
        {
            try
            {
                if (counter == null)
                {
                    LogError("Performance counter not set up or installed! Please verify installation!", EventIDCodes.SystemEvents, "Performance counter is not set up", "N/A");
                }
                else
                {
                    counter.IncrementBy(incrementBy);
                }
            }
            catch (Exception ex)
            {
                LogError(ex.ToString(), EventIDCodes.SystemEvents, "Increment performance counter", counter.MachineName);
            }
        }
        #endregion

        #region eventLogDbDAL Events
        private void eventLogDbDAL_RaiseDBAccess()
        {
            IncrementCounter(dbAccessPerSecond);
        }
        private void eventLogDbDAL_RaiseDBAccessErrors()
        {
            IncrementCounter(errorsPerSecond);
        }
        private void eventLogDbDAL_RaiseDBAccessInsertDuplicates()
        {
            IncrementCounter(dbDuplicateInsertsPerSecond);
        }
        private void eventLogDbDAL_RaiseDBAccessInserts(int inserts)
        {
            IncrementCounter(dbInsertsPerSecond, inserts);
        }
        private void eventLogDbDAL_RaiseInfoMessage(string message)
        {
            try
            {
                Log.WriteEntry(message, EventLogEntryType.Information, (int)EventIDCodes.DatabaseError);
            }
            catch { }
        }
        private void eventLogDbDAL_RaiseErrorMessage(string message)
        {
            try
            {
                Log.WriteEntry(message, EventLogEntryType.Error, (int)EventIDCodes.DatabaseError);
            }
            catch { }
        }
        private void eventLogDbDAL_RaiseSyncInfoMessage(string message)
        {
            //if (thisCollector.LogSyncInfoMessages)
            //{
            //    try
            //    {
            //        Log.WriteEntry(message, EventLogEntryType.Information, (int)EventIDCodes.DataImport);
            //    }
            //    catch { }
            //}
        }
        private void eventLogDbDAL_DBAccessDurationInfoMessage(long milliseconds)
        {
            //IncrementCounter(dbAccessDurationMilliseconds, milliseconds);
        }
        #endregion

        #region TraceDuration
        private void TraceDuration(string methodName, string stepName, double totalSeconds, string details)
        {
            Trace.WriteLine(string.Format("{0} sec - {1} - {2} - {3}", totalSeconds, methodName, stepName, details));
        } 
        #endregion

    }
}
